#include "task.h"

#include <linux/jiffies.h>

#include "timer.h"

static inline void farjmp(u16 seg, u16 offset) {
    // __asm__ __volatile__("ljmp *%0\n"
    //                      :
    //                      : "m"((struct {
    //                          unsigned int off;
    //                          unsigned short sel;
    //                      }){offset, seg}));
}

static struct TASKCTL *s_taskctl = NULL;
struct TIMER g_task_timer;

struct TASK *task_alloc(void) {
    struct TASK *task;
    int i;
    for (i = 0; i < MAX_TASKS; i++) {
        if (s_taskctl->tasks0[i].flags == 0) {
            task = &s_taskctl->tasks0[i];
            task->flags = TASK_SLEEPING;

            // 入口地址，由于内核使用的是段内相对地址，我们定义text.first在0x00280000
            // 所以函数需要减去这个地址才得到真正的段内偏移
            // task->tss.x86_tss.flags = 0x00000202; /* IF = 1 */
            // task->tss.x86_tss.ax = 0;
            // task->tss.x86_tss.cx = 0;
            // task->tss.x86_tss.dx = 0;
            // task->tss.x86_tss.bx = 0;
            // task->tss.x86_tss.sp = 0;
            // task->tss.x86_tss.bp = 0;
            // task->tss.x86_tss.si = 0;
            // task->tss.x86_tss.di = 0;
            // task->tss.x86_tss.es = 0;
            // task->tss.x86_tss.cs = 0;
            // task->tss.x86_tss.ss = 0;
            // task->tss.x86_tss.ds = 0;
            // task->tss.x86_tss.fs = 0;
            // task->tss.x86_tss.gs = 0;
            // task->tss.x86_tss.ldt = 0;
            return task;
        }
    }
    return NULL;
}

static int get_task_idx(struct TASK *task) { return (task - s_taskctl->tasks0) / sizeof(struct TASK); }

struct TASK *task_init(struct MEMMAN *memman) {
    int i;
    struct TASK *task = NULL;
    // struct desc_struct *gdt = (struct desc_struct *)ADR_GDT;
    // s_taskctl = (struct TASKCTL *)memman_alloc_4k(memman, sizeof(struct TASKCTL));
    // for (i = 0; i < MAX_TASKS; i++) {
    //     s_taskctl->tasks0[i].flags = TASK_UNUSED;
    //     s_taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;

    //     tss_desc desc;
    //     set_tssldt_descriptor(&desc, (unsigned long)&s_taskctl->tasks0[i].tss.x86_tss, DESC_TSS, __KERNEL_TSS_LIMIT);
    //     write_gdt_entry(gdt, TASK_GDT0 + i, &desc, DESC_TSS);
    // }
    // task = task_alloc();
    // task->flags = TASK_RUNNING;
    // task->priority = 2;  // 0.02s
    // s_taskctl->running = 1;
    // s_taskctl->now = get_task_idx(task);
    // s_taskctl->tasks[s_taskctl->now] = task;
    // load_tr(task->sel);
    // timer_init(&g_task_timer, NULL, 0);
    // timer_settime(&g_task_timer, jiffies + msecs_to_jiffies(10 * task->priority));
    return task;
}

void task_run(struct TASK *task, int priority) {
    if (priority > 0) {
        task->priority = priority;
    }
    if (task->flags != TASK_RUNNING) {
        task->flags = TASK_RUNNING;
        s_taskctl->tasks[s_taskctl->running] = task;
        s_taskctl->running++;
    }
}

void taskswitch(void) {
    struct TASK *task;
    s_taskctl->now++;
    if (s_taskctl->now >= s_taskctl->running) {
        s_taskctl->now = 0;
    }

    task = s_taskctl->tasks[s_taskctl->now];
    timer_settime(&g_task_timer, jiffies + msecs_to_jiffies(1 * task->priority));
    if (s_taskctl->running > 1) {
        farjmp(task->sel, 0);
    }
}

void task_sleep(struct TASK *task) {
    if (task->flags != TASK_RUNNING) {
        return;
    }
    // 仅处理当前运行状态的进程

    int i;            // 取要sleep的进程在tasks数组中的位置
    bool ts = false;  // 标识是否要休眠当前正在运行的进程
    if (task == s_taskctl->tasks[s_taskctl->now]) {
        // 想要sleep当前正在运行的进程，需要先切换到其他进程
        ts = true;
        i = s_taskctl->now;
    } else {
        // 想要sleep其他进程，找到其位置
        for (i = 0; i < s_taskctl->running; i++) {
            if (s_taskctl->tasks[i] == task) {
                // 这个时候i就是task在tasks数组中的位置
                break;
            }
        }
    }
    --s_taskctl->running;
    if (i < s_taskctl->now) {
        // 要sleep的进程在当前正在运行的进程之前，当前进程的索引会变
        s_taskctl->now--;
    }
    // 移动成员
    for (; i < s_taskctl->running; i++) {
        s_taskctl->tasks[i] = s_taskctl->tasks[i + 1];
    }
    task->flags = TASK_SLEEPING;
    if (ts) {
        // 要sleep当前正在运行的进程，需要先切换到其他进程
        if (s_taskctl->now >= s_taskctl->running) {
            // 修正一下now
            s_taskctl->now = 0;
        }
        farjmp(s_taskctl->tasks[s_taskctl->now]->sel, 0);
    }
}
